package co.edu.uniandes.ecos.monitoractividad;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

import co.edu.uniandes.ecos.entidades.CasaBloqueada;
import co.edu.uniandes.ecos.entidades.ColaMonitor;

public class ServidorCallBack extends Thread
{
	private static ArrayList<CasaBloqueada> listaNegra =  new ArrayList<CasaBloqueada>();
	private int puertoEscucha;
	
	public ServidorCallBack(int puertoEscucha)
	{
		this.puertoEscucha = puertoEscucha;
	}
	
	public void run()
	{
		ServerSocket serverSocket = null;
		try 
		{
			serverSocket = new ServerSocket(this.puertoEscucha);
			System.out.println("CallBack Escuchando en puerto: " + this.puertoEscucha);
		} 
		catch (IOException e) 
		{
			System.err.println("No se puede escuchar en puerto: " + this.puertoEscucha + ". " + e.getMessage());
			System.exit(-1);
		}		
		
		// se inicia el hilo desencolador de las casas bloqueadas
		new HiloDesencolador().start();
		
		while (true) 
		{
			try 
			{
				//TODO: Hscerlo con ThreadPool porque es muy cosotso crear cada hilo en tiempo de eejcucion
				new HiloEvento(serverSocket.accept()).start();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Valida si la casa esta en la lista negra de casas bloqueadas
	 * @param casa
	 * @return
	 */
	public static synchronized boolean validarEnLista(String casa)
	{		
		for(CasaBloqueada s: listaNegra)
		{
			if(casa.equals(s.casa))
			{
				return true;
			}			
		}
		
		return false;
	}
	
	/**
	 * Agrega una casa para que se bloquee por intento de DoS
	 * @param casa
	 */
	public static synchronized void agregarElementoLista(CasaBloqueada casa)
	{
		listaNegra.add(casa);
	}
	
	/*
	 * Revisar todas las casas bloqueadas para saber a cuales ya se les puede seguir recibiendo mensajes porque ya se ha terminado su tiempo maximo
	 */
	public static synchronized void desencolarXTiempo()
	{
		long tiempoActual = System.currentTimeMillis();
		//long minutosQueSeDebeBloquearUnaCasa = 1000 * 60 * 10;
		long minutosQueSeDebeBloquearUnaCasa = 1000 * 15;
		
		int i = 0;
		while(i < ServidorCallBack.listaNegra.size() && ServidorCallBack.listaNegra.size() > 0)
		{
			CasaBloqueada c = ServidorCallBack.listaNegra.get(i);
			
			if(tiempoActual - c.desdeCuandoEstaBloqueada > minutosQueSeDebeBloquearUnaCasa)
			{
				ServidorCallBack.listaNegra.remove(i);
				System.out.println("[HiloDesbloqueadorCasa] Se ha desbloqueado la casa: " + c.casa);
			}
			else 
				i++;
		}		
	}

}

/**
 * Encargado de trabajar los mensaje que llegan desde los servidores de Urbanizacion (o sea los CallBack)
 * @author PabloAndres
 *
 */
class HiloEvento extends Thread 
{
	private Socket socketCliente;	

	public HiloEvento(Socket cliente) 
	{
		this.socketCliente = cliente;
	}
	
	public void run()
	{
		try 
		{
			socketCliente.setSoTimeout(100);
			BufferedReader in = new BufferedReader(new InputStreamReader(socketCliente.getInputStream()));		
			String mensaje = in.readLine();
			in.close();
			String tipoMensaje = mensaje.substring(0, 2);
			
			if(tipoMensaje.equals("01")) // Se ha recibido un OK de trabajo de una tarea terminada en el servidor de urbanizacion
			{
				String codigoCasa = mensaje.substring(2, 6);				
				String timestamp = mensaje.substring(6, 26);
				
				System.out.println("[ServidorCallBack] Trabajo de Casa terminado. Casa: " + codigoCasa);
				
				synchronized(this)
				{
					int indiceEncontrado = -1;
					for(int i=0; i<Proxy.alertasEnviadas.size(); i++)
					{
						ColaMonitor m = Proxy.alertasEnviadas.get(i);
						if(m.casa.equals(codigoCasa) && m.timestamp.equals(timestamp))
						{
							indiceEncontrado = i;
							break;
						}
					}
					
					if(indiceEncontrado > -1)
					{
						Proxy.alertasEnviadas.remove(indiceEncontrado);
					}
				}
			}
			else if(tipoMensaje.equals("02")) // Se debe bloquear una casa por intento de DoS
			{
				String codigoCasa = mensaje.substring(2, 6);
				String timestamp = mensaje.substring(6, 26);
				
				System.out.println("[ServidorCallBack] Bloqueando casa: " + codigoCasa);
				
				CasaBloqueada casaBloqueada = new CasaBloqueada();
				casaBloqueada.casa = codigoCasa;
				casaBloqueada.desdeCuandoEstaBloqueada = System.currentTimeMillis();
				
				ServidorCallBack.agregarElementoLista(casaBloqueada);
				
				synchronized(this)
				{
					int indiceEncontrado = -1;
					for(int i=0; i<Proxy.alertasEnviadas.size(); i++)
					{
						ColaMonitor m = Proxy.alertasEnviadas.get(i);
						if(m.casa.equals(codigoCasa) && m.timestamp.equals(timestamp))
						{
							indiceEncontrado = i;
							break;
						}
					}
					
					if(indiceEncontrado > -1)
					{
						Proxy.alertasEnviadas.remove(indiceEncontrado);
					}
				}
			}
			else if(tipoMensaje.equals("03")) // Se ha suscrito un nuevo servidor de urbanizacion
			{
				// llega el mensaje con el puerto en donde esta escuchando el servidor que se ha suscrito
				
				String puertoDeServidorUrbanizacionSubscribiendose = mensaje.substring(2, 7);
				
				String ipLocal = socketCliente.getRemoteSocketAddress().toString().split(":")[0];
				ipLocal = ipLocal.substring(1, ipLocal.length());
				
				String datosServidorSubscrito = String.format("%s;%s", ipLocal, puertoDeServidorUrbanizacionSubscribiendose);
				
				int indiceExiste = Proxy.listadoServidoresUrbanizacion.indexOf(datosServidorSubscrito);
				if(indiceExiste == -1)
				{
					System.out.println("[ServidorCallBack] Inscribiendo servidor en puerto: " + puertoDeServidorUrbanizacionSubscribiendose);
					Proxy.listadoServidoresUrbanizacion.add(datosServidorSubscrito);
				}
			}
			
			this.socketCliente.close();
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		}
	}
}

/**
 * Encargado de desencolar la lista negra de casas bloqueadas para desbloquearlas en caso que ya se haya pasado su tiempo maximo de bloqueo
 * @author PabloAndres
 *
 */
class HiloDesencolador extends Thread 
{
	public HiloDesencolador() 
	{
		
	}
	
	public void run() 
	{
		while (true) 
		{
			try 
			{
				Thread.sleep(1000);
				synchronized (this) 
				{					
					ServidorCallBack.desencolarXTiempo();
				}
			} 
			catch (Exception e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

